#include <assert.h>

#include "http.h"

HTTPRequest newEmptyRequest() {
    const HTTPRequest req = {
        .method = NULL,
        .url = NULL,
        .protocol = NULL,
        .headers = newEmptyHeaders(),
        .path = NULL,
        .queryString = NULL,
        ._useFileBody = false,
        ._binaryBody = NULL,
        ._fileBody = NULL,
        ._currentBodyPos = 0,
        ._bodySize = 0,
    };
    return req;
}

// 释放HttpRequest中的动态分配的内存
void freeHTTPRequest(HTTPRequest *request) {
    assert(request != NULL);
    if (request->method) free(request->method);
    if (request->url) free(request->url);
    if (request->protocol) free(request->protocol);
    if (request->path) free(request->path);
    if (request->queryString) free(request->queryString);
    if (request->_fileBody) fclose(request->_fileBody);
    if (request->_binaryBody) free(request->_binaryBody);
    freeHeaders(&request->headers);
    request->method = NULL;
    request->url = NULL;
    request->protocol = NULL;
    request->path = NULL;
    request->queryString = NULL;
    request->_fileBody = NULL;
    request->_binaryBody = NULL;
    request->_bodySize = 0;
    request->_useFileBody = false;
    request->_currentBodyPos = 0;
}

bool makeEmptyRequest(HTTPRequest *req) {
    assert(req != NULL);
    freeHTTPRequest(req);
    return true;
}

// 处理HTTP协议
bool recvHTTPRequestFromSocket(const SOCKET client, HTTPRequest *httpRequest) {
    assert(httpRequest != NULL);
    constexpr int maxReadSize = 65535;
    char readBuffer[maxReadSize];
    int lineSize = 0;
    char *method = NULL,*url = NULL,*protocol=NULL,*path=NULL,*queryString=NULL, *binaryBody=NULL;
    FILE *fileBody = NULL;

    // 读取第一行：格式为"method url protocol\r\n"
    lineSize = readLine(client, readBuffer, maxReadSize);
    if (lineSize == SOCKET_ERROR) goto onError;
    if (!strEndsWith(readBuffer, "\r\n")) goto onError;
    // 解析 method
    const char *space1 = strchr(readBuffer, ' ');
    if (space1 == NULL) goto onError;
    const size_t methodLength = space1 - readBuffer;
    if (methodLength == 0) goto onError;
    method = malloc(methodLength + 1);
    if (method == NULL) goto onError;
    strncpy(method, readBuffer, methodLength);
    method[methodLength] = '\0';
    // 解析 rawUrl
    const char *space2 = strchr(space1 + 1, ' ');
    if (space2 == NULL) goto onError;
    const size_t urlLength = space2 - (space1 + 1);
    if (urlLength == 0) goto onError;
    url = malloc(urlLength + 1);
    if (url == NULL) goto onError;
    strncpy(url, space1 + 1, urlLength);
    url[urlLength] = '\0';
    // 解析 protocol
    const char *protocolStart = space2 + 1;
    const size_t originLength = strlen(protocolStart);
    if (originLength <= 2) goto onError;
    const size_t protocolLength = originLength - 2;
    protocol = malloc(protocolLength + 1);
    if (protocol == NULL) goto onError;
    strncpy(protocol, protocolStart, protocolLength);
    protocol[protocolLength] = '\0';
    // 解析 path 和 query
    const char *queryStart = strchr(url, '?');
    if (queryStart == NULL) {
        path = strdup(url);
        queryString = strdup("");
    } else {
        const size_t pathLength = queryStart - url;
        if (pathLength == 0) goto onError;
        path = malloc(pathLength + 1);
        if (path == NULL) goto onError;
        strncpy(path, url, pathLength);
        path[pathLength] = '\0';
        const size_t queryLength = strlen(queryStart + 1);
        if (queryLength == 0) {
            queryString = strdup("");
        } else {
            queryString = malloc(queryLength + 1);
            if (queryString == NULL) goto onError;
            strncpy(queryString, queryStart+1, queryLength);
            queryString[queryLength] = '\0';
        }
    }

    // 解析HTTP头部：格式为"key: value\r\n"，结束标志为\r\n
    while (true) {
        lineSize = readLine(client, readBuffer, maxReadSize);
        if (lineSize == SOCKET_ERROR) goto onError;
        if (!strEndsWith(readBuffer, "\r\n")) goto onError;
        if (strcmp(readBuffer, "\r\n") == 0) break;
        const char *pos = strstr(readBuffer, ": ");
        if (pos == NULL) goto onError;
        // 解析key
        const size_t keyLength = pos - readBuffer;
        if (keyLength == 0) goto onError;
        char *key = malloc(keyLength + 1);
        if (key == NULL) goto onError;
        strncpy(key, readBuffer, keyLength);
        key[keyLength] = '\0';
        // 解析value
        const size_t valueLength = strlen(readBuffer) - keyLength - 4;
        char *value;
        if (valueLength == 0) {
            value = strdup("");
        } else {
            value = malloc(valueLength + 1);
            if (value == NULL) goto onError;
            strncpy(value, pos+2, valueLength);
            value[valueLength] = '\0';
        }
        const HTTPHeader header = {
            .key = key,
            .value = value,
        };
        if (!appendHeader(&httpRequest->headers, header)) {
            free(key);
            free(value);
            goto onError;
        }
    }

    // 将请求体读取到binaryBody中（<128kb）,或者写入tmp_file(>=128kb)
    const unsigned long bodySize = getContentLength(&httpRequest->headers);
    bool useFileBody = false;
    if (bodySize == 0) {
        useFileBody = false;
        binaryBody = NULL;
        fileBody = NULL;
    } else if (bodySize <= 128 * 1024) {
        binaryBody = malloc(bodySize);
        if (binaryBody == NULL) goto onError;
        if (recv(client, binaryBody, bodySize, MSG_WAITALL) == SOCKET_ERROR) goto onError;
        useFileBody = false;
        fileBody = NULL;
    } else {
        fileBody = tmpfile();
        if (fileBody == NULL) goto onError;
        if(!socketToFile(client, fileBody, bodySize)) goto onError;
        fseek(fileBody, 0, SEEK_SET);
        useFileBody = true;
        binaryBody = NULL;
    }

    // 保存数据
    httpRequest->method = method;
    httpRequest->url = url;
    httpRequest->protocol = protocol;
    httpRequest->path = path;
    httpRequest->queryString = queryString;
    httpRequest->_fileBody = fileBody;
    httpRequest->_binaryBody = binaryBody;
    httpRequest->_bodySize = bodySize;
    httpRequest->_useFileBody = useFileBody;
    return true;

    // 异常处理
    onError:
        if (method) free(method);
        if (url) free(url);
        if (protocol) free(protocol);
        if (path) free(path);
        if (queryString) free(queryString);
        if (fileBody) fclose(fileBody);
        if (binaryBody) free(binaryBody);
        freeHeaders(&httpRequest->headers);
        return false;
}

void seekRequestBody(HTTPRequest *httpRequest, const unsigned long offset) {
    assert(offset <= httpRequest->_bodySize);
    httpRequest->_currentBodyPos = offset;
}

size_t readBodyFromRequest(const HTTPRequest *httpRequest, char *buffer, const size_t bufferSize) {
    if (httpRequest->_useFileBody) {
        if (httpRequest->_fileBody == NULL) return 0;
        fseek(httpRequest->_fileBody, httpRequest->_currentBodyPos, SEEK_SET);
        return fread(buffer, sizeof(char), bufferSize, httpRequest->_fileBody);
    }

    if (httpRequest->_binaryBody == NULL) return 0;
    const char *src = httpRequest->_binaryBody + httpRequest->_currentBodyPos;
    const size_t size = httpRequest->_bodySize - httpRequest->_currentBodyPos;
    return memcpy(buffer, src, size) != NULL? size : 0;
}